package com.ddw.meedeeuh.presenters;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.ListModel;

import com.ddw.meedeeuh.MeedeeuhApp;
import com.ddw.meedeeuh.shared.events.DBMovieSelectedEvent;
import com.ddw.meedeeuh.shared.events.DBMoviesChangedEvent;
import com.ddw.meedeeuh.shared.events.DBMoviesChangedEventHandler;
import com.ddw.meedeeuh.shared.events.RefreshDBMoviesEvent;
import com.ddw.meedeeuh.shared.events.StartMovieImportEvent;
import com.ddw.meedeeuh.shared.events.StartMovieScrapeEvent;
import com.ddw.meedeeuh.shared.movie.database.DBMovie;
import com.ddw.meedeeuh.shared.mvp.PresenterBase;
import com.ddw.meedeeuh.shared.mvp.View;

public class MoviesPresenter extends PresenterBase<MoviesPresenter.MoviesView> {

	private final MoviesListModel model = new MoviesListModel();

	public MoviesPresenter(MeedeeuhApp app, MoviesView view) {
		super(app, view);
	}

	public static interface MoviesView extends View {

		public void setListModel(ListModel<ListModelMovie> model);

		public void addSearchListener(ActionListener listener);

		public String getSearchText();

		public void clearSearchText();

		public void setSearchMessage(String message);

		public void addRefreshListener(ActionListener listener);

		public void addImportListener(ActionListener listener);

		public void addScrapeListener(ActionListener listener);

		public void addMovieSelectionListener(ActionListener listener);

		public boolean validateScrape();

		public boolean validateImport();

		public ListModelMovie getSelectedMovie();

	}

	@Override
	public void onSetup() {
		// Setup the model for the list
		getView().setListModel(getModel());

		// Listen for search events
		getView().addSearchListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// For string comparison use lower case matching
				final String searchText = getView().getSearchText().trim().toLowerCase();

				// If there is no search string just show all movies
				if(searchText.length() == 0) {
					getModel().setMovies(getApp().getDbMovies().values());
					return;
				}

				// Loop through and try to find movies
				final List<DBMovie> filteredMovies = new ArrayList<DBMovie>();
				for(final DBMovie movie : getApp().getDbMovies().values()) {
					if(movie.getName() != null && movie.getName().toLowerCase().contains(searchText)) {
						filteredMovies.add(movie);
					}
				}

				// Check we have more than zero results
				if(filteredMovies.size() == 0) {
					getView().setSearchMessage("The search returned no results");
					return;
				}

				// Add the filtered movies
				getModel().setMovies(filteredMovies);
			}

		});

		// Listen for refresh events
		getView().addRefreshListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				getView().clearSearchText();
				getApp().getEventBus().post(new RefreshDBMoviesEvent());
			}

		});

		// Listen for import events
		getView().addImportListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(getView().validateImport()) {
					getApp().getEventBus().post(new StartMovieImportEvent());
				}
			}

		});

		// Listen for scrape events
		getView().addScrapeListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if(getView().validateScrape()) {
					getApp().getEventBus().post(new StartMovieScrapeEvent());
				}
			}

		});

		// Listen for global movie change events
		getApp().getEventBus().register(new DBMoviesChangedEventHandler() {

			@Override
			public void onDBMoviesChanged(DBMoviesChangedEvent e) {
				getModel().setMovies(e.getMovies().values());
			}

		});

		// Listen for movie selection
		getView().addMovieSelectionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				final ListModelMovie selectedMovie = getView().getSelectedMovie();
				if(selectedMovie != null) {
					getApp().getEventBus().post(new DBMovieSelectedEvent(selectedMovie.getMovie()));
				}
			}

		});

		// Set the initial movies
		getModel().setMovies(getApp().getDbMovies().values());
	}

	private MoviesListModel getModel() {
		return this.model;
	}

	public static interface ListModelMovie {

		public DBMovie getMovie();

	}

	private static class ListModelMovieImpl implements ListModelMovie {

		private static final String LABEL_FORMAT = "<html><font style='color:%s'>%s</font>%s</html>";
		private static final String YEAR_FORMAT = " <font style='font-style:italic;font-weight:normal;color:%s'>(%d)</font>";
		private final DBMovie movie;
		private String toString = null;
		private Boolean complete = null;

		public ListModelMovieImpl(DBMovie movie) {
			this.movie = movie;
		}

		@Override
		public String toString() {
			if(this.toString == null) {
				final String titleColour = isComplete() ? "#000000" : "#cc3300";
				final String yearColour = isComplete() ? "#888888" : "#ee5522";
				final String yearFormat = this.movie.getYear() != null ? String.format(YEAR_FORMAT, yearColour, this.movie.getYear()) : "";
				this.toString = String.format(LABEL_FORMAT, titleColour, this.movie.getName(), yearFormat);
			}
			return this.toString;
		}

		@Override
		public DBMovie getMovie() {
			return this.movie;
		}

		private boolean isComplete() {
			if(this.complete == null) {
				this.complete = getMovie().getName() != null &&
						getMovie().getYear() != null &&
						getMovie().getNfo() != null &&
						getMovie().getPoster() != null &&
						getMovie().getFanart() != null &&
						getMovie().getExtraThumbsCount() >= 5;
			}
			return this.complete;
		}

	}

	@SuppressWarnings("serial")
	private class MoviesListModel extends AbstractListModel<ListModelMovie> {

		private final List<ListModelMovie> movies = new ArrayList<ListModelMovie>();
		private final MovieNameYearComparator comparator = new MovieNameYearComparator();

		@Override
		public ListModelMovie getElementAt(int index) {
			return movies.get(index);
		}

		@Override
		public int getSize() {
			return movies.size();
		}

		public void setMovies(Collection<DBMovie> movies) {
			this.movies.clear();
			for(final DBMovie movie : movies) {
				this.movies.add(new ListModelMovieImpl(movie));
			}
			Collections.sort(this.movies, this.comparator);
			fireContentsChanged(this, 0, getSize());
		}

		private class MovieNameYearComparator implements Comparator<ListModelMovie> {

			@Override
			public int compare(ListModelMovie m1, ListModelMovie m2) {
				final String n1 = getMovieSortName(m1.getMovie());
				final String n2 = getMovieSortName(m2.getMovie());

				return n1.compareTo(n2);
			}

			private String getMovieSortName(DBMovie movie) {
				return (movie.getName() == null ? "" : movie.getName()) + " " +
					(movie.getYear() == null ? "" : movie.getYear().toString());
			}

		}

	}

}
